Skip to main content

¿Qué es Expo GL y Three.js?

expo-three te permite crear experiencias 3D con la biblioteca Three.js en aplicaciones desarrolladas en Expo, facilitando la implementación de gráficos tridimensionales en diferentes plataformas.

expo-gl es una interfaz para WebGL en Expo que sirve como base para proyectos 3D, incluyendo expo-three, ofreciendo compatibilidad con renderizados 3D tanto en la web como en dispositivos móviles.

expo-three

  • Integración de Three.js con Expo: expo-three actúa como un puente entre Expo y Three.js, simplificando el uso de esta potente biblioteca gráfica en aplicaciones Expo.
  • Experiencias 3D universales: Esta biblioteca permite crear aplicaciones 3D interactivas compatibles con dispositivos móviles y navegadores web.
  • Manejo del DOM en Three.js: expo-three abstrae la gestión del DOM, facilitando la creación y renderización de escenas 3D sin preocuparte por configuraciones específicas de Three.js.

expo-gl

  • WebGL para Expo: expo-gl proporciona una interfaz para WebGL, tecnología clave para renderizados 3D en navegadores y móviles.
  • Rendimiento de gráficos nativos con OpenGL-ES: expo-gl se basa en OpenGL-ES, estándar de gráficos 3D en dispositivos móviles, asegurando alto rendimiento y calidad visual en tus aplicaciones 3D.

Instalaciones necesarias

Para comenzar a utilizar expo-three y expo-gl, primero instala los paquetes en tu proyecto Expo ejecutando el siguiente comando:

npm i three expo-three expo-gl

Configuración de Metro

Expo utiliza Metro para empaquetar y gestionar los activos en las aplicaciones. Para que expo-three pueda cargar modelos y texturas adicionales, configura metro.config.js para admitir tipos de archivos adicionales.

Si aún no tienes este archivo en tu proyecto, créalo con el siguiente comando:

npx expo customize metro.config.js

En metro.config.js, agrega las extensiones de archivo requeridas para los modelos y texturas que se cargarán en Three.js:

// Aprende más en https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require('expo/metro-config');

/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);

// Agregar extensiones para modelos y texturas
config.resolver.assetExts.push("obj", "mtl", "xpng", "xjpg", "ttf");

module.exports = config;

Implementación básica

GLView - El "Lienzo" de Renderizado

Para renderizar modelos 3D en Expo, utiliza GLView, el cual actúa como el canvas en el que se renderizan las escenas 3D. La propiedad onContextCreate es crucial, ya que es donde se inicializa el contexto de renderizado.

 <View style={{ flex: 1 }}>
<GLView
style={{ height: "100%", width: "100%" }}
onContextCreate={onContextCreate}
/>
</View>

Configuración de onContextCreate

onContextCreate debe ser una función asíncrona que devuelve una promesa. Este es el núcleo de la configuración de tu escena, cámara y renderizador.

const onContextCreate = async (gl: ExpoWebGLRenderingContext): Promise<void> => {
const scene = new Scene();
const camera = new PerspectiveCamera(
75,
gl.drawingBufferWidth / gl.drawingBufferHeight,
0.1,
1000
);
camera.position.z = 2;

const ambientLight = new AmbientLight(0x00ff00, 1);
scene.add(ambientLight);

gl.canvas = {
width: gl.drawingBufferWidth,
height: gl.drawingBufferHeight,
};

const renderer = new Renderer({ gl });
renderer.setSize(gl.drawingBufferWidth, gl.drawingBufferHeight);

const render = (): void => {
requestAnimationFrame(render);
renderer.render(scene, camera);
gl.endFrameEXP();
};

render();
};

Cargar Modelos y Texturas

Compatibilidad con modelos OBJ y texturas PNG

Expo tiene ciertas limitaciones al cargar modelos 3D como glb, gltf y fbx, lo que hace que los modelos obj sean una opción más compatible para su uso con expo-three.

Para cargar el modelo y la textura, utiliza Asset de expo-asset y aplica el cambio de extensión de .png a .xpng en las texturas.

Nota: Cambiar la extensión de las texturas de .png a .xpng evita problemas de ruta en Android, ya que Expo almacena las texturas en una ubicación diferente cuando se empaqueta la aplicación para producción (apk). En desarrollo, los archivos de textura .png se encuentran en la carpeta assets/textura, pero en un archivo apk la ruta cambia, lo que hace que Expo no pueda encontrarlos. Cambiando la extensión a .xpng, Expo asegura que se mantenga la ruta original independientemente de si estás en desarrollo o en producción.

// Cargar modelo y textura desde assets
const [{ localUri: localObj }] = await Asset.loadAsync(require("../assets/model/Costal1.obj"));
const [{ localUri: textures }] = await Asset.loadAsync(require("../assets/model/texture/TXT_ESPECIALIDAD_CANINO_DIGESTIVEHEALTH_AL.xpng"));

Crear Material con Textura

Después de cargar la textura, crea un material con TextureLoader y configura los filtros de textura. Esto mejora el rendimiento y hace que los modelos tengan un aspecto más detallado.

const textureModel = new TextureLoader().load(textures);
textureModel.magFilter = NearestFilter;
textureModel.minFilter = NearestFilter;

const material = new MeshPhongMaterial({
map: textureModel,
flatShading: true,
emissiveIntensity: 0,
shininess: 0,
reflectivity: 0,
});

Cargar el Modelo OBJ

Usa OBJLoader para cargar el modelo 3D en la escena. Asigna el material al modelo y configura propiedades adicionales.

const setObjectProperties = ({ object, material, name }) => {
object.name = name;
object.position.set(0, -20, -38);
object.rotation.set(0, 0, 0);

object.traverse((child) => {
if (child instanceof Mesh) child.material = material;
});
};

const loader = new OBJLoader();
loader.load(
localObj,
(object) => {
setObjectProperties({ object, material, name: "costal" });
scene.add(object);
},
undefined,
(error) => console.error("Error loading OBJ model", error)
);

Código Completo

Este es el código completo con toda la configuración mencionada:

import {
AmbientLight,
Mesh,
MeshPhongMaterial,
NearestFilter,
PerspectiveCamera,
Scene,
Object3D,
} from "three";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
import { Asset } from "expo-asset";
import ExpoTHREE, { Renderer, TextureLoader } from "expo-three";
import { ExpoWebGLRenderingContext } from "expo-gl";

const useGLV = () => {
const setObjectProperties = ({ object, material, name }) => {
object.name = name;
object.position.set(0, -20, -38);
object.rotation.set(0, 0, 0);

object.traverse((child) => {
if (child instanceof Mesh) child.material = material;
});
};

const onContextCreate = async (gl) => {
const scene = new Scene();
const camera = new PerspectiveCamera(
75,
gl.drawingBufferWidth / gl.drawingBufferHeight,
0.1,
1000
);
camera.position.z = 2;

const ambientLight = new AmbientLight(0x00ff00, 1);
scene.add(ambientLight);

gl.canvas = {
width: gl.drawingBufferWidth,
height: gl.drawingBufferHeight,
};

const [{ localUri: localObj }] = await Asset.loadAsync(require("../assets/model/Costal1.obj"));
const [{ localUri: textures }] = await Asset.loadAsync(require("../assets/model/texture/TXT_ESPECIALIDAD_CANINO_DIGESTIVEHEALTH_AL.xpng"));

const textureModel = new TextureLoader().load(textures);
textureModel.magFilter = NearestFilter;
textureModel.minFilter = NearestFilter;
const material = new MeshPhongMaterial({
map: textureModel,
flatShading: true,
emissiveIntensity: 0,
shininess: 0,
reflectivity: 0,
});

const loader = new OBJLoader();
loader.load(
localObj,
(object) => {
setObjectProperties({ object, material, name: "costal" });
scene.add(object);


},
undefined,
(error) => console.error("Error loading OBJ model", error)
);

const renderer = new Renderer({ gl });
renderer.setSize(gl.drawingBufferWidth, gl.drawingBufferHeight);

const render = () => {
requestAnimationFrame(render);
renderer.render(scene, camera);
gl.endFrameEXP();
};

render();
};

return { onContextCreate };
};

export default useGLV;

Consideraciones adicionales

  • Nombres de archivos duplicados: Evita duplicados en nombres de archivos en diferentes formatos (por ejemplo, modelo.obj y modelo.mtl) para prevenir errores en la compilación a APK.
  • Contexto de onContextCreate: Debido a que onContextCreate vive en su propio contexto, evita pasar estados o referencias de otros componentes.

Recuerso extras